import datetime
from typing import Union
from fastapi import Header, HTTPException, Request, Depends, status
from fastapi.security import OAuth2PasswordBearer
from jose import jwt, JWTError

from core.config import settings, REDIS_CLIENT
from models import ZhanUser, ZhanToken
from models.chat import ChatExclusiveKey
from services.user_services.user import UserVerifyService


async def get_token_header(x_token: str):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


async def get_query_token(token: str):
    if token != "jessica":
        raise HTTPException(status_code=400, detail="No Jessica token provided")


async def get_key_info(user):
    chat_key = await ChatExclusiveKey.filter(user_id=user.id, expiration_time__lte=datetime.datetime.now()).first()
    if chat_key:
        if chat_key.use_count >= chat_key.count:
            return None
        chat_key.use_count += 1
        await chat_key.save()
        return True


oauth2_scheme = OAuth2PasswordBearer(tokenUrl="user/login")


def encode_user_data(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=settings.ALGORITHM)
        username: str = payload.get("sub")
        pk: int = payload.get("pk")
        now: int = payload.get("now")
        exp: int = payload.get("exp")
    except JWTError:
        return None, None, None, None
    return username, pk, now, exp


async def get_current_user(token: str = Depends(oauth2_scheme)):
    """
    未检测到用户会抛出错误
    :param token:
    :return:
    """
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    token_item = await ZhanToken.filter(token=token)
    if not token_item:
        raise credentials_exception
    username, pk, now, exp = encode_user_data(token)
    if username is None:
        raise credentials_exception
    user = await UserVerifyService.get_user(username, pk)
    if not UserVerifyService.verify_time(now, exp):
        raise credentials_exception
    if user is None:
        raise credentials_exception
    return user


async def get_current_admin_user(current_user: ZhanUser = Depends(get_current_user)):
    """
    检测是否为admin用户
    :param current_user:
    :return:
    """
    if current_user.status != ZhanUser.USER_STATUS_OPEN or current_user.authority != ZhanUser.AUTHORITY_ADMIN:
        raise HTTPException(status_code=400, detail="Invalid user")
    return current_user


async def get_current_active_user(current_user: ZhanUser = Depends(get_current_user)):
    """
    检测是否为活动用户
    :param current_user:
    :return:
    """
    if current_user.status == ZhanUser.USER_STATUS_CLOSE:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user


async def get_user(token: str = Depends(oauth2_scheme)):
    """
    取消错误response抛出，适用于该接口可以不登录也可以使用
    :param token:
    :return:
    """
    username, pk, now, exp = encode_user_data(token)
    if username is None:
        return None
    token_item = await ZhanToken.filter(token=token)
    if not token_item:
        return None
    user = await UserVerifyService.get_user(username, pk)
    if not UserVerifyService.verify_time(now, exp):
        return None
    if user is None:
        return None
    if user.status == ZhanUser.USER_STATUS_CLOSE:
        return None
    return user


async def get_request_ip_key(request: Request, user: ZhanUser = Depends(get_user)):
    if user:
        result = await get_key_info(user)
        if result:
            return
        key = f"{user.username}{user.id}"
        result = REDIS_CLIENT.get(key)
        if not result:
            REDIS_CLIENT.set(key, 1, ex=settings.RESET_FREE_TIME)
            return
        else:
            if int(result) < settings.FREE_ACCOUNT_COUNT:
                ttl = REDIS_CLIENT.ttl(key)
                REDIS_CLIENT.set(key, int(result) + 1, ex=ttl)
                return

    result = REDIS_CLIENT.get(request.client.host)
    if not result:
        REDIS_CLIENT.set(request.client.host, 1, ex=settings.RESET_FREE_TIME)
    else:
        if int(result) >= settings.FREE_COUNT:
            raise HTTPException(status_code=400, detail="Free times have been used up")
        ttl = REDIS_CLIENT.ttl(request.client.host)
        REDIS_CLIENT.set(request.client.host, int(result) + 1, ex=ttl)
